/*******************************************************************************
 * Copyright (c) 2000, 2004 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Common Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package safeCommitter.core.view.dialogs;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;

import org.eclipse.compare.CompareConfiguration;
import org.eclipse.compare.CompareViewerPane;
import org.eclipse.compare.IEncodedStreamContentAccessor;
import org.eclipse.compare.ITypedElement;
import org.eclipse.compare.contentmergeviewer.TextMergeViewer;
import org.eclipse.compare.structuremergeviewer.DiffNode;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.DialogSettings;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.TextAttribute;
import org.eclipse.jface.text.TextPresentation;
import org.eclipse.jface.text.TextViewer;
import org.eclipse.jface.text.presentation.IPresentationDamager;
import org.eclipse.jface.text.presentation.IPresentationReconciler;
import org.eclipse.jface.text.presentation.IPresentationRepairer;
import org.eclipse.jface.text.presentation.PresentationReconciler;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.jface.text.source.SourceViewerConfiguration;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

import safeCommitter.test.report.SCTestResult;

/**
 * Small modifications to fit TestNG.
 * 
 * @author <a href='mailto:the_mindstorm@evolva.ro'>Alexandru Popescu</a>
 */
public class CompareResultDialog extends Dialog {

	/*
	 * workaround - to make prefix and suffix accessible to the
	 * CompareResultViewerConfiguration
	 */
	private static CompareResultDialog fgThis;

	// dialog store id constants
	private final static String DIALOG_BOUNDS_KEY = "CompareResultDialogBounds"; //$NON-NLS-1$
	private static final String X = "x"; //$NON-NLS-1$
	private static final String Y = "y"; //$NON-NLS-1$
	private static final String WIDTH = "width"; //$NON-NLS-1$
	private static final String HEIGHT = "height"; //$NON-NLS-1$

	private TextMergeViewer fViewer;
	private String fExpected;
	private String fActual;
	private final String testName;

	private int fPrefix;
	private int fSuffix;
	protected Rectangle fNewBounds;

	private DialogSettings fSettings;

	public CompareResultDialog(Shell parentShell, SCTestResult failure) {
		super(parentShell);
		fgThis = this;
		setShellStyle(getShellStyle() | SWT.RESIZE | SWT.MAX);
		testName = failure.getTestName();
		initializeActualExpected(failure.getStackTrace());
		computePrefixSuffix();
	}

	// TESTNG-21
	private void initializeActualExpected(String trace) {
		String firstToken = "expected:<";
		String nextTokenString = "> but was:<";
		int idxStart = trace.indexOf(firstToken);
		if (idxStart == -1) {
			firstToken = "expected not same with:<";
			idxStart = trace.indexOf(firstToken);
			nextTokenString = "> but was same:<";
		}
		if (idxStart == -1) {
			firstToken = "expected same with:<";
			idxStart = trace.indexOf(firstToken);
			nextTokenString = "> but was:<";
		}

		if (idxStart != -1) {
			int idxEnd = trace.indexOf(nextTokenString); // trace.indexOf('>',
			// idxStart);
			fExpected = trace.substring(idxStart + firstToken.length(), idxEnd);
			idxStart = idxEnd + nextTokenString.length();
			fActual = trace.substring(idxStart, trace.lastIndexOf('>'));
		} else {
			fActual = "N/A";
			fExpected = "N/A";
		}
	}

	@Override
	protected Point getInitialSize() {
		int width = 0;
		int height = 0;

		final Shell s = getShell();
		if (s != null) {
			s.addControlListener(new ControlListener() {
				public void controlMoved(ControlEvent arg) {
					fNewBounds = s.getBounds();
				}

				public void controlResized(ControlEvent arg) {
					fNewBounds = s.getBounds();
				}
			});
		}
		IDialogSettings bounds = null;
		if (bounds == null) {
			return super.getInitialSize();
		} else {
			try {
				width = bounds.getInt(WIDTH);
			} catch (NumberFormatException e) {
				width = 400;
			}
			try {
				height = bounds.getInt(HEIGHT);
			} catch (NumberFormatException e) {
				height = 300;
			}
		}

		return new Point(width, height);
	}

	@Override
	protected Point getInitialLocation(Point initialSize) {
		Point loc = super.getInitialLocation(initialSize);

		return loc;
	}

	@Override
	public boolean close() {
		boolean closed = super.close();
		if (closed && fNewBounds != null) {
			saveBounds(fNewBounds);
		}

		return closed;
	}

	private void saveBounds(Rectangle bounds) {
		IDialogSettings dialogBounds = null;
		if (dialogBounds == null) {
			dialogBounds = new DialogSettings(DIALOG_BOUNDS_KEY);
			fSettings.addSection(dialogBounds);
		}
		dialogBounds.put(X, bounds.x);
		dialogBounds.put(Y, bounds.y);
		dialogBounds.put(WIDTH, bounds.width);
		dialogBounds.put(HEIGHT, bounds.height);
	}

	private void computePrefixSuffix() {
		int end = Math.min(fExpected.length(), fActual.length());
		int i = 0;
		for (; i < end; i++) {
			if (fExpected.charAt(i) != fActual.charAt(i)) {
				break;
			}
		}
		fPrefix = i;

		int j = fExpected.length() - 1;
		int k = fActual.length() - 1;
		int l = 0;
		for (; k >= fPrefix && j >= fPrefix; k--, j--) {
			if (fExpected.charAt(j) != fActual.charAt(k)) {
				break;
			}
			l++;
		}
		fSuffix = l;
	}

	@Override
	protected void configureShell(Shell newShell) {
		super.configureShell(newShell);
		newShell.setText("Compare"); //$NON-NLS-1$
	}

	@Override
	protected void createButtonsForButtonBar(Composite parent) {
		createButton(parent, IDialogConstants.OK_ID, "OK", true); //$NON-NLS-1$
	}

	@Override
	protected Control createDialogArea(Composite parent) {
		Composite composite = (Composite) super.createDialogArea(parent);
		GridLayout layout = new GridLayout();
		layout.numColumns = 1;
		composite.setLayout(layout);

		CompareViewerPane pane = new CompareViewerPane(composite, SWT.BORDER
				| SWT.FLAT);
		pane.setText(testName);
		GridData data = new GridData(GridData.FILL_HORIZONTAL
				| GridData.FILL_VERTICAL);
		data.widthHint = convertWidthInCharsToPixels(120);
		data.heightHint = convertHeightInCharsToPixels(13);
		pane.setLayoutData(data);

		Control previewer = createPreviewer(pane);
		pane.setContent(previewer);
		GridData gd = new GridData(GridData.FILL_BOTH);
		previewer.setLayoutData(gd);
		applyDialogFont(parent);

		return composite;
	}

	private Control createPreviewer(Composite parent) {
		final CompareConfiguration compareConfiguration = new CompareConfiguration();
		compareConfiguration.setLeftLabel("Expected"); //$NON-NLS-1$
		compareConfiguration.setLeftEditable(false);
		compareConfiguration.setRightLabel("Actual"); //$NON-NLS-1$
		compareConfiguration.setRightEditable(false);
		compareConfiguration.setProperty(
				CompareConfiguration.IGNORE_WHITESPACE, Boolean.FALSE);

		fViewer = new CompareResultMergeViewer(parent, SWT.NONE,
				compareConfiguration);
		fViewer.setInput(new DiffNode(new CompareElement(fExpected),
				new CompareElement(fActual)));

		Control control = fViewer.getControl();
		control.addDisposeListener(new DisposeListener() {
			public void widgetDisposed(DisposeEvent e) {
				if (compareConfiguration != null) {
					compareConfiguration.dispose();
				}
			}
		});

		return control;
	}

	private static class CompareResultMergeViewer extends TextMergeViewer {
		private CompareResultMergeViewer(Composite parent, int style,
				CompareConfiguration configuration) {
			super(parent, style, configuration);
		}

		@Override
		protected void configureTextViewer(TextViewer textViewer) {
			if (textViewer instanceof SourceViewer) {
				((SourceViewer) textViewer)
						.configure(new CompareResultViewerConfiguration());
			}
		}
	}

	public static class CompareResultViewerConfiguration extends
			SourceViewerConfiguration {

		@Override
		public IPresentationReconciler getPresentationReconciler(
				ISourceViewer sourceViewer) {
			PresentationReconciler reconciler = new PresentationReconciler();
			SimpleDamagerRepairer dr = new SimpleDamagerRepairer();
			reconciler.setDamager(dr, IDocument.DEFAULT_CONTENT_TYPE);
			reconciler.setRepairer(dr, IDocument.DEFAULT_CONTENT_TYPE);

			return reconciler;
		}

		public static class SimpleDamagerRepairer implements
				IPresentationDamager, IPresentationRepairer {
			private IDocument fDocument;

			public void setDocument(IDocument document) {
				fDocument = document;
			}

			public IRegion getDamageRegion(ITypedRegion partition,
					DocumentEvent event, boolean changed) {
				return new Region(0, fDocument.getLength());
			}

			public void createPresentation(TextPresentation presentation,
					ITypedRegion damage) {
				int suffix = CompareResultDialog.fgThis.fSuffix;
				int prefix = CompareResultDialog.fgThis.fPrefix;
				TextAttribute attr = new TextAttribute(Display.getDefault()
						.getSystemColor(SWT.COLOR_RED), null, SWT.BOLD);
				presentation.addStyleRange(new StyleRange(prefix, fDocument
						.getLength()
						- suffix - prefix, attr.getForeground(), attr
						.getBackground(), attr.getStyle()));
			}
		}
	}

	private static class CompareElement implements ITypedElement,
			IEncodedStreamContentAccessor {
		private final String fContent;

		public CompareElement(String content) {
			fContent = content;
		}

		public String getName() {
			return "<no name>"; //$NON-NLS-1$
		}

		public Image getImage() {
			return null;
		}

		public String getType() {
			return "txt"; //$NON-NLS-1$
		}

		public InputStream getContents() {
			try {
				return new ByteArrayInputStream(fContent.getBytes("UTF-8")); //$NON-NLS-1$
			} catch (UnsupportedEncodingException e) {
				return new ByteArrayInputStream(fContent.getBytes());
			}
		}

		public String getCharset() throws CoreException {
			return "UTF-8"; //$NON-NLS-1$
		}
	}
}
